/** @file    BundleGeneric.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BundleGeneric.cpp 2873 2010-08-08 19:06:41Z alex $
 *  @brief
 * File containing methods for the wosh::BundleGeneric class.
 * The header for this class can be found in BundleGeneric.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/BundleGeneric.h>

 #include <core/ObjectAllocator.h>
 #include <core/SecurityManager.h>
 #include <core/Thread.h>
 #include <core/BundleManager.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
                                                  #include <core/MessageProcessingCommon.h>
 #include <core/SystemError.h>
 #include <core/Utilities.h>
 #include <core/BundleRequirements.h>
 #include <core/PropertiesProvider.h>
 #include <core/InterfacesProvider.h>
 #include <core/MethodsProvider.h>
 #include <core/Table.h>
 #include <core/List.h>
 #include <core/DataModels.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER_INTERFACE(wosh::Bundle, "wosh::interfaces::IBundle", 1.00, _static_Bundle1 )
 WOSH_REGISTER_INTERFACE(wosh::Bundle, "wosh::Object", 1.00, _static_Bundle2 )
 WOSH_REGISTER_INTERFACE(wosh::BundleGeneric, "wosh::Bundle", 1.00, _static_BundleGeneric )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BundleGeneric::BundleGeneric( wosh::Object* parent )
	: Bundle(parent), Credentials(NULL) {
	Object::setName( _BundleGeneric_NAME );
	Bundle::setBundleState( Bundle::STATE_CREATING, false );

	Log.setIndent( 3 );
	Log.setContext( "BundleGeneric" );
	Log.setLevel( LOG_INFO );
//Log.setLevel( LOG_VERBOSE );

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	// initialize permissions of default properties, callback-handler will be assigned at setBundleState(CREATED)
	Properties.set( _KEY_type,			BundleGeneric::className(), Permission(Permission::Read) );
	Properties.set( _KEY_Version,		0.0, Permission(Permission::Read) );
	Properties.set( _KEY_DebugLevel,	LogContext::getLogLevelAsString(LOG_INFO), Permission::RW_RW_R__() );
	Properties.set( _KEY_interfaces,	BundleGeneric::className(), Permission(Permission::Read) );
	Properties.set( _Bundle_KEY_State,	getBundleStateAsString(Bundle::STATE_CREATING), Permission(Permission::Read) );
	Properties.setPermission( Object::getPermission(), Permission::Overwrite_All, true, true );
	Properties.monitor(_KEY_name);
	Properties.monitor(_KEY_DebugLevel);

	Log(LOG_DEBUG, " Registering methods.." );
	Methods.setPermission( Object::getPermission(), Permission::Overwrite_All, true, true );
	Methods.getListPermission().setMask( Permission::Read, Permission::Read, Permission::Read );

	MethodRequest* mmBundleStart = Methods.create<MethodRequest>( _Bundle_METHOD_Start, "Start Bundle", Permission::R_XR_XR__() );
	mmBundleStart->setMethod( this, (MethodRequestPtr)&BundleGeneric::mmDoBundleStart );

	MethodRequest* mmBundleStop = Methods.create<MethodRequest>( _Bundle_METHOD_Stop, "Stop Bundle", Permission::R_XR_XR__() );
	mmBundleStop->setMethod( this, (MethodRequestPtr)&BundleGeneric::mmDoBundleStop );

	MethodRequest* mmBundleRestart = Methods.create<MethodRequest>( _Bundle_METHOD_Restart, "Restart Bundle", Permission::R_XR_XR__() );
	mmBundleRestart->setMethod( this, (MethodRequestPtr)&BundleGeneric::mmDoBundleRestart );

	MethodWRESULT* mmBundleDiag = Methods.create<MethodWRESULT>( _Bundle_METHOD_Diagnostic, "Diagnostic Bundle", Permission::R_XR_XR__() );
	mmBundleDiag->setMethod( this, (MethodWRESULTPtr)&BundleGeneric::bundleDiagnostic );

	MethodPropertyT::createMethods( &Methods, &Properties, &Log );

	MethodChT::createMethods( &Methods, this, &Properties, &Log );

	MethodList* mmListMethods = Methods.create<MethodList>( _METHOD_ListMethods, "List Methods", Permission(Permission::RX) );
	mmListMethods->setDataFieldExtractor( DataFieldExtractor<Method>::createFor< DataAdapterMapT<std::string,Method> >(Methods, "Bundle Methods") );

	MethodList* mmListInterfaces = Methods.create<MethodList>( _METHOD_ListInterfaces, "List Interfaces", Permission(Permission::RX) );
	mmListInterfaces->setDataFieldExtractor( DataFieldExtractor<ObjectTypeInfo>::createFor< DataAdapterMapT<std::string,ObjectTypeInfo> >(Interfaces, "Bundle Interfaces") );
	MethodRetrieveObject<InterfacesProvider>* mmGetInterfaces = Methods.create< MethodRetrieveObject<InterfacesProvider> >( _METHOD_GetInterfacesProvider, "Get InterfacesProvider object", Permission(Permission::RX) );
	mmGetInterfaces->configure(&Interfaces, NULL, NULL);

	MethodList* mmListRequirements = Methods.create<MethodList>( _METHOD_ListRequirements, "List Requirements", Permission(Permission::RX) );
	mmListRequirements->setDataFieldExtractor( DataFieldExtractor<BundleRequirement>::createFor< DataAdapterListT<BundleRequirement> >(Requirements, "Bundle Requirements") );
	MethodRetrieveObject<BundleRequirements>* mmGetRequirements = Methods.create< MethodRetrieveObject<BundleRequirements> >( _METHOD_GetRequirementsProvider, "Get Requirements", Permission(Permission::RX) );
	mmGetRequirements->configure(&Requirements, NULL, NULL);

	// Bundle will be registered to BUS (Core) when Bundle::STATE_CREATED is raised (by implementations)
	// so more initialization may be done before being connected to bus (and start receiving messages).
	// implementations must call "setBundleState(Bundle::STATE_CREATED, false);" before exiting their own constructor
 }

BundleGeneric::~BundleGeneric() {
	setCredentials(NULL);
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT BundleGeneric::start_SynchThread( Thread* thread, unsigned long timeout ) {
	if ( thread == NULL ) return WRET_ERR_PARAM;
	if ( getBundleState() == Bundle::STATE_STARTED ) {
		// seems we are already running, let's ensure thread is running!
		if ( thread->isThreadRunning() ) {
			SystemError::raise(this, Log, SystemError::WARNING, 0, "start_SynchThread", "Bundle is already running, as its thread [%s]", thread->getThreadName().c_str() );
			return WRET_ERR_ILLEGAL_USE;
		 }
		else
			SystemError::raise(this, Log, SystemError::WARNING, 0, "start_SynchThread", "BundleState and Thread are different! (ignoring bundle state and trying to start %s..)", thread->getThreadName().c_str() );
	 }
	else if ( getBundleState() == Bundle::STATE_STARTING ) {
		// seems we was just starting.. let's wait a while and check again
		ThreadImpl::sleepForSec(1);
		if ( !thread->isThreadRunning() )
			Log(LOG_WARNING, ":start_SynchThread() : Trying to start thread again (wait-5 mode)!" );
		WRESULT ret = thread->startThread(timeout);
		if ( WFAILED(ret) ) {
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "start_SynchThread", "FAILED waiting RUNNING state of thread.." );
			return WRET_ERR_INTERNAL;
		 }
	 }
	else if ( getBundleState() == Bundle::STATE_STOPPING ) {
		// seems we was just stopping.. let's wait a while and check again
		ThreadImpl::sleepForSec(5);
		SystemError::raise(this, Log, SystemError::WARNING, 0, "start_SynchThread", "Trying to start while Bundle is stopping.." );
	 }

	if ( !thread->isThreadRunning() ) {
		setBundleState( Bundle::STATE_STARTING );
		Log(LOG_DEBUG, ":start_SynchThread() : Starting Thread [%s]..", thread->getThreadName().c_str() );
		WRESULT ret = thread->startThread(timeout);
		if ( WFAILED(ret) ) {
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "start_SynchThread", "FAILED waiting RUNNING state of thread.." );
			return WRET_ERR_INTERNAL;
		 }
	 }
	else // force state..
		setBundleState( Bundle::STATE_STARTED );
	return WRET_OK;
 }

WRESULT BundleGeneric::stop_SynchThread( Thread* thread, unsigned long timeout ) {
	if ( thread == NULL ) return WRET_ERR_PARAM;
	if ( getBundleState() == Bundle::STATE_STOPPED ) {
		// seems we are not running now, let's ensure thread is stopped!
		if ( !thread->isThreadRunning() ) {
			SystemError::raise(this, Log, SystemError::WARNING, 0, "stop_SynchThread", "Bundle is already stopped, as its internal thread" );
			return WRET_ERR_ILLEGAL_USE;
		 }
		else
			SystemError::raise(this, Log, SystemError::WARNING, 0, "stop_SynchThread", "BundleState and Thread are different! (ignoring bundle state and trying to stop thread..)" );
	 }
	else if ( getBundleState() == Bundle::STATE_STOPPING ) {
		// seems we was just closing.. let's wait a while and check again
		ThreadImpl::sleepForSec(1);
		if ( thread->isThreadRunning() )
			Log(LOG_WARNING, ":stop_SynchThread() : Trying to stop thread again (wait-5 mode)!" );
		WRESULT ret = thread->quitThread(timeout);
		if ( WFAILED(ret) ) {
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "stop_SynchThread", "FAILED waiting STOPPED state of thread..[now: %s]", thread->getThreadStateAsString() );
			return WRET_ERR_INTERNAL;
		 }
	 }
	else if ( getBundleState() == Bundle::STATE_STARTING ) {
		// seems we was just starting.. let's wait a while and check again
		ThreadImpl::sleepForSec(5);
		Log(LOG_WARNING, ":stop_SynchThread() : Trying to stop while Bundle is starting.." );
	 }
	if ( thread->isThreadRunning() ) {
		setBundleState( Bundle::STATE_STOPPING );
		Log(LOG_DEBUG, ":stop_SynchThread() : Stopping Thread [%s]..", thread->getThreadName().c_str() );
		WRESULT ret = thread->quitThread(timeout);
		if ( WFAILED(ret) ) {
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "stop_SynchThread", "FAILED waiting STOPPED state of thread.." );
			return WRET_ERR_INTERNAL;
		 }
	 }
	else // force state..
		setBundleState( Bundle::STATE_STOPPED );
	return WRET_OK;
 }

bool BundleGeneric::bundleValidate_StartStop( Bundle::BUNDLE_STATE state ) {
	if ( state == Bundle::STATE_STARTING ) {
		if ( getBundleState() != Bundle::STATE_CREATED
				&& getBundleState() != Bundle::STATE_STARTING && getBundleState() != Bundle::STATE_STARTED
				&& getBundleState() != Bundle::STATE_STOPPING && getBundleState() != Bundle::STATE_STOPPED ) {
			Log(LOG_CRITICAL, ":bundleValidate_StartStop(%s) : Wrong status, current state: %s", getBundleStateAsString(state), getBundleStateAsString() );
			return false;
		 }
		// so we are in:  CREATED | STARTING | STARTED | STOPPING | STOPPED
		return true;
	 }
	else if ( state == Bundle::STATE_STOPPING ) {
		if ( getBundleState() != Bundle::STATE_STARTING && getBundleState() != Bundle::STATE_STARTED
				&& getBundleState() != Bundle::STATE_STOPPING && getBundleState() != Bundle::STATE_STOPPED ) {
			Log(LOG_CRITICAL, ":bundleStop() : Wrong status, current state: %s", getBundleStateAsString() );
			return false;
		 }
		// so we are in:  STARTING | STARTED | STOPPING | STOPPED
		return true;
	 }
	Log(LOG_WARNING, ":bundleValidate_StartStop(%s) : UnHandled status", getBundleStateAsString(state) );
	return false;
 }

WRESULT BundleGeneric::bundleRestart() {
	WRESULT ret = bundleStop();
	if ( WFAILED(ret) )
		return ret;
	bool stopped = BundleManager::waitState( this, Bundle::STATE_STOPPED, 15000 );
	if ( !stopped ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleRestart", "FAILED waiting STOPPED state.." );
		return WRET_ERR_INTERNAL;
	 }
	ret = bundleStart();
	bool started = BundleManager::waitState( this, Bundle::STATE_STARTED, 15000 );
	if ( !started ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleRestart", "FAILED waiting STARTED state.." );
		return WRET_ERR_INTERNAL;
	 }
	return ret;
 }

WRESULT BundleGeneric::bundleDiagnostic() {
	WRESULT ret = WRET_OK;
	Log(LOG_VERBOSE, ":bundleDiagnostic() : Starting BundleGeneric-Diagnostic" );
	// check Name
	if ( Object::getName().empty() ) {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "Diagnostic", "FATAL Empty Object::Name!" );
		ret += WRET_ERR_INTERNAL;
	 }
	else if ( Object::getName() == "wosh.BundleGeneric" )
		SystemError::raise(this, Log, SystemError::WARNING, 0, "Diagnostic", "FATAL Default Object::Name! [wosh.BundleGeneric]" );

	// check BusCore..
	bool bus_connected = BusCore.isConnected();
	if ( bus_connected )
		Log(LOG_VERBOSE, " Diagnostic : BusCore[wosh.Bus.Core] is Connected." );
	else {
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "Diagnostic", "FATAL BusCore[wosh.Bus.Core] is DISConnected!" );
		ret += WRET_ERR_INTERNAL;
	 }
	Log(LOG_INFO, ":bundleDiagnostic() : Finished BundleGeneric-Diagnostic [%d]", ret );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleGeneric::bundleSnapshotState( long id ) {
	WRESULT ret = WRET_OK;
	Log(LOG_VERBOSE, ":bundleSnapshotState(%d) : Starting BundleGeneric-SnapshotState", id );

	Log(LOG_INFO, ":bundleSnapshotState(%d) : Finished BundleGeneric-SnapshotState [%d]", id, ret );
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES

void BundleGeneric::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	if ( !message.getContent()->isRequest() ) return;
	if ( getBundleState() == Bundle::STATE_DESTROYING )	return;
	if ( !MessageFilter::isMessageTarget(message, this) ) return;
	Message* reply = MPC_busMessageRequest( message, this->Methods, this->Log );
	if ( reply == NULL ) return;
	if ( !reply->hasSecurityToken() )
		signMessage(reply);
	//else reply->setSource(this);
	if ( source != NULL )
		source->postMessage(reply);
	else
		BusCore.postMessage(reply);
 }

//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS

bool BundleGeneric::updatingProperty( bool& , const Variant& value_proposed, Property& property_current, const PropertiesProvider* ) {
	// permission check is done by get/set/list methods, so the operation is generally allowed
	// basically we just need to apply the new settings for real
	if ( property_current.getKey() == _KEY_name ) { // if we are here, we allow to edit name
		Log(LOG_VERBOSE, " Changing Bundle Name, Log-Context will change to '%s'", value_proposed.toString().c_str() );
		Object::setName(value_proposed.toString());
		Log.setContext(value_proposed.toString());
		return true;
	 }
	else if ( property_current.getKey() == _KEY_DebugLevel )
		return MPC_PropertyParse_LogLevel( value_proposed, this->Log );
	// not handled, sound strange but probably its ok since we are the lower (base) implementation
	return true;
 }

///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* BundleGeneric::mmDoBundleStart( const Request* request ) {
	if ( request == NULL ) return NULL;
	Log(LOG_VERBOSE, ":mmDoBundleStart()");
	WRESULT ret = bundleStart(); // this is synch!
	return request->replyResponse(ret, getBundleStateAsString());
 }

Response* BundleGeneric::mmDoBundleStop( const Request* request ) {
	if ( request == NULL ) return NULL;
	Log(LOG_VERBOSE, ":mmDoBundleStop()");
	WRESULT ret = bundleStop(); // this is synch!
	return request->replyResponse(ret, getBundleStateAsString());
 }

Response* BundleGeneric::mmDoBundleRestart( const Request* request ) {
	if ( request == NULL ) return NULL;
	Log(LOG_VERBOSE, ":mmDoBundleRestart()");
	WRESULT ret = bundleRestart(); // this is synch!
	return request->replyResponse(ret, getBundleStateAsString());
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// COMMON/HELPERS

void BundleGeneric::setName( const std::string& value, bool updateURI ) {
	Object::setName(value);
	Properties.transactionBeginWrite();
	Properties.update( _KEY_name, value );
	Properties.transactionEnd();
	Log.setContext(value);
	Log(LOG_VERBOSE, ":setName('%s')", value.c_str() );
	if ( updateURI && !Object::getURI().isGlobal() ) {
		Log(LOG_VERBOSE, ":setName() Overwriting URI [%s]..", Object::getURI().toString().c_str() );
		Object::getURI().setName(value);
		Object::getURI().registerGlobal();
	 }
 }

void BundleGeneric::setBundleState( Bundle::BUNDLE_STATE state, bool raise_event ) {
	Bundle::setBundleState( state, raise_event );
	Properties.transactionBeginWrite();
	Properties.update( _Bundle_KEY_State, getBundleStateAsString(state) );
	Properties.transactionEnd();
	if ( state == Bundle::STATE_CREATED ) {
		// gather the implemented interfaces
		Interfaces.createFor(this);
		// update objects-keys and supported interfaces
		Properties.set( _KEY_type, getBundleType(), Permission(Permission::Read) );
		Properties.set( _KEY_interfaces, Interfaces.toString(), Permission(Permission::Read) );
		Properties.updateObjectKeys( this );
		// register myself for Properties-callbacks
		Properties.setPropertiesProviderListener(this);
		// register to BUS
		BusCore.setMessageHandler(this);
		WRESULT bus_connected = BusCore.connect( _Bus_Core, Bus::ACTION_DEFERRED );
		if ( WFAILED(bus_connected) )
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "setBundleState", "[CREATED] FAILED#%d Connecting Bus (wosh.Bus.Core)..", bus_connected );
	 }
 }

void BundleGeneric::setPermission( const Permission& value ) {
	Log(LOG_DEBUG, ":setPermission('%s')", value.toUnixStyle().c_str() );
	Object::setPermission( value );
	Properties.updateObjectKeys( this );

	// fix child-permissions
	Properties.setPermission( value, Permission::Overwrite_All, true, true );
	Methods.setPermission( value, Permission::Overwrite_All, true, true );
 }

void BundleGeneric::setCredentials( SecurityTokenProvider* credentials ) {
	if ( this->Credentials != NULL ) delete this->Credentials;
	this->Credentials = credentials;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

SecurityToken* BundleGeneric::signMessage( Message* message, const SecurityToken* source_token ) const {
	if ( message == NULL ) return NULL;
	message->setSource(this); // 
	if ( source_token != NULL ) // we are forwarding a message, it's important to keep track of source security token
		return SecurityManager::getCredentialProxy().signMessage(message, source_token, this); // source token will be encapsulated
	if ( bundleCredentials() == NULL ) // the bundle has a custom SecurityTokenProvider, lets use that one
		return SecurityManager::getCredentialImplicit().signMessage(message, this);
	return bundleCredentials()->signMessage(message, this); // the 'implicit' security token
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<Bundle>* BundleGeneric::getDataFields() {
	DataFieldCollector<Bundle>* fields = new DataFieldCollector<Bundle>();
//	fields->setDataSetName("Bundles");
	fields->add<const std::string&, Variant>(&Bundle::getName, "Name", 0);
	fields->add<const char*, Variant>(&Bundle::getClassName, "Type", 1);
	fields->add<const char*,Variant>(&Bundle::getBundleStateAsString, "State", 2);
	fields->add<const URI&,URI>(&Bundle::getURI, "URI", 4);
	DataFieldSubWrapperT<Bundle, const InterfacesProvider*, List*, void>* dfwInterfacesList;
	dfwInterfacesList = new DataFieldSubWrapperT<Bundle, const InterfacesProvider*, List*, void>(&Bundle::bundleInterfaces, &InterfacesProvider::toList, "Interfaces", 3);
	dfwInterfacesList->info.visible = false;
	fields->add(dfwInterfacesList);
	fields->add<const std::string&,Variant>(&Bundle::getEntityID, "EntityID", 5, false);
	fields->add<const Permission&,Permission>(&Bundle::getPermission, "Permission", 6, false);
	fields->add<Bundle::BUNDLE_STATE,Variant>(&Bundle::getBundleState, "State[int]", 2, false); // duplicate but different type
	DataFieldSubWrapperT<Bundle, const InterfacesProvider*, std::string, Variant>* dfwInterfacesStr;
	dfwInterfacesStr = new DataFieldSubWrapperT<Bundle, const InterfacesProvider*, std::string, Variant>(&Bundle::bundleInterfaces, &InterfacesProvider::toString, "Interfaces[Str]", 3);
//	dfwInterfacesStr->info.visible = false;
	fields->add(dfwInterfacesStr);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS

void BundleGeneric::thread_event( Thread::THREAD_STATE thread_event, Thread* thread_source ) {
	if ( thread_source != NULL )
		Log(LOG_DEBUG, ":thread_event(%d, %ld) : [%s] Raised state %s", (int)thread_event, thread_source->getThreadID(), thread_source->getThreadName().c_str(), Thread::getThreadStateAsString(thread_event) );
	else
		Log(LOG_DEBUG, ":thread_event(%d,NULL) UNKNOWN thread raised state %s", (int)thread_event, Thread::getThreadStateAsString(thread_event) );
	switch(thread_event) {
		case Thread::STATE_RUNNING: {
				setBundleState( Bundle::STATE_STARTED );
				break;
			 }
		case Thread::STATE_STOPPED: {
				if ( this->getBundleState() != Bundle::STATE_STOPPING ) {
					// probably it was an error!
					SystemError::raise(this, SystemError::WARNING, 0, "thread_event(STATE_STOPPED) (state!=STATE_STOPPING)", &Log);
				 }
				setBundleState( Bundle::STATE_STOPPED );
				break;
			 }
		case Thread::STATE_CRASHED:
		case Thread::STATE_FAILURE: {
			SystemError::raise(this, SystemError::CRITICAL, 0, "thread_event(STATE_FAILURE)", &Log);
				setBundleState( Bundle::STATE_STOPPED );
				break;
			 }
		default: break;
	 }
 }

/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
